在 Dart 中所有東西都是一個物件 - 每一個儲存在變數的「值」都是某個類別的物件實例。所有物件都繼承自 Object
類別。這種設計使 Dart 成為一種純粹的物件導向程式語言。
要建立一個類別的物件(實例化),必須使用一個特殊的方法稱為建構子(constructor)。
int
float
等都是物件且繼承自 Object
類別。表示它們是可以被覆寫修改行為。class Point {
double x, y;
Point(this.x, this.y); // 建構子簡化語法糖
// 非簡化版本
// Point(double x, double y) {
// this.x = x;
// this.y = y;
// }
Point.origin(): x = 0, y = 0; // 具名建構子
}
void main() {
var p1 = Point(1, 2);
var p2 = Point.origin();
}
除了類別之外,Dart 也包含其他物件導向的智慧
封裝是物件導向程式設計的重要原則之一,它讓我們可以隱藏實作細節,只揭露必要的介面。
實務上,Dart 的封裝是在函式庫級別實現的,而不是類別級別。
Dart 沒有明確指定欄位或方法存取範圍的功能,像 Java 有 protected
,private
,public
等。Dart 會為全部類別中的欄位建立隱式的 getter 和 setter,因此你可以通過這些 getter 和 setter 控制類別的屬性。
class Person {
String name;
int _age;
Person(this.name, this._age);
}
void main() {
var person = Person('Alice', 20);
print(person.name); // 可
print(person._age); // 如果不在同一檔案,不能直接訪問 _age,因為它是私有的。但是同一個庫/檔案是可以的。
}
在 Dart 如果一個類別,類別成員,全域函式或者變數使用 _
開頭的話就是專屬於該函式庫(私有)。
關於私有屬於檔案層級的範例:
class Person {
String name;
int _age;
Person(this.name, this._age);
}
class Employee {
void age(Person person) {
print(person._age); // 當在同一個檔案的時候可以存取
}
}
main() {
var person = Person('Alice', 20);
var employee = Employee();
employee.age(person); // 可以使用 _age
}
繼承可以讓我們擴展一個類別的定義。
mixins
可以擴展類別的功能而不依賴繼承。extends
關鍵字定義繼承。final
final class Point {
final double x;
final double y;
Point(this.x, this.y);
}
標記 final
表示該類別不能被繼承。類別中的成員 x
和 y
也被標記為 final
,這表示這些成員一旦被初始化後,它們的值就不能被改變。
繼承外,還有抽象類別用來定義類別的特徵,而不直接實作。
implements
而不是繼承interface
類別修飾子class Car {
void drive() {
//
}
}
// 實作類別介面
class ElectricCar implements Car {
@override
void drive() {}
}
abstract class Vehicle {
void drive();
}
class Car implements Vehicle {
@override
void drive() {}
}
interface IVehicle {
void drive();
}
class ElectricCar implements IVehicle {
@override
void drive() {}
}
當你想要保留某個類別的功能進而擴展時可以使用繼承 extends
,一個類別只能繼承一個父類別;單一繼承
而實現 implements
介面則是用來強制某個類別遵守某個規範,實現介面不會繼承任何已經實作的程式碼,必須自己實現。
抽象類別和介面的差異:抽象類別可以包含局部實作和局部規範,可共享程式碼。
至於介面只能定義方法簽章。
- 繼承:只能單一繼承表示 is-a 的關係,通過
extends
實現,子類別會獲得父類別的功能可以直接使用或覆寫- 介面:是一種表示 behaves-as 的關係,在 Dart 類別也可以作為介面通過
implements
語法實現。一個類別可以實現多個介面,必須提供介面宣告的全部方法- 混入(mixin):使用
with
實現,用於共用方法,不適合單一繼承或介面規範重複一樣的實作。class Vehicle { void drive() { print('move'); } } class Car implements Vehicle { @override void drive() { print('move with gas'); } } class ElectricCar extends Car { @override void drive() { print('move silently'); } } mixin AutoDriving { void navigate() { print('navigating'); } } class Tesla extends ElectricCar with AutoDriving { @override void drive() { super.drive(); navigate(); } }
Mixin 是 Dart 中重用類別代碼的一種方式,不需要複雜的繼承層次:
mixin Swimmable {
void swim() {
print('Swimming');
}
}
class Duck extends Animal with Swimmable {}
多型簡單的說就是物件允許被視為父類別的一種物件實例,可視為一個物件表現的像另一個物件的能力例如 int 也是一種 num。
通過覆寫方法,子類可以改變父類同名方法的行為,也就是在呼叫的時候可視為同一個動作,但是具體實現不一樣。
另外則是要注意:Dart 不支援方法重載,你不能用不同參數定義兩個同名的方法,如果有這種需求嘗試使用可選,位置,具名參數的方式來實現,也就是同一個方法支援各種參數。
會提到不支援方法重載 overload 的重點在於我們可以通過例如可選參數的方式,在維持一樣參數的情況下達成各種需要的覆寫。
物件導向的三大特性:封裝、繼承、多型(多態)
public
private
這些實作,Dart 是檔案層級。move
方法但是細節卻不同,一個變數可以 Animal a = Dog()
如此一來變數都可以執行 a.move()
卻有不同的表現形式。Dart 稱為真物件導向語言,在 Dart 中函式也是物件,意味著:
這被稱為 first-class functions 因為它們可以和其他的型別一樣,並且在 Flutter 中常被用來建立 Widget。
Dart 的物件導向特性提供了強大而靈活的工具來組織和構建你的代碼。
隨著你在 Flutter 開發中的深入,你會發現這些物件導向的概念如何幫助你構建複雜的 UI 和管理應用狀態。如果是第一次從腳本類型語言踏進物件導向的人來說,Dart 的類別結構和語法可能會顯得繁瑣和複雜。確實可能帶來一定的學習曲線。然而,儘管初期可能感到困難,但掌握 OOP 的概念和技巧將可以提升程式的可維護性、可重用性和擴展性。